
#include "de_music.h"

#include "../public/public_msg/public_msg.h"

//****************************************************************

#define CFG_PCM_SIZE	(256*4)

//****************************************************************

static d_music_para_t d_mp;

//****************************************************************

void* music_read_thread(void *arg)
{
	int       ret = -1;

	while(1)
	{
		usleep(1000);
		//*************************************
		ret = av_read_frame(d_mp.a_format,d_mp.a_packet);
		if(ret){
			d_mp.is_end = 1;
			av_packet_unref(d_mp.a_packet);
			log_e("msg:%s\n",av_err2str(ret));
			pthread_exit(NULL);
			return -1;
		}	
		//*************************************
		if(d_mp.a_packet->stream_index == d_mp.a_index)
		{
			if(d_mp.a_packet->pts <= 0){
				d_mp.a_packet->pts = 1;
			}
			queue_packet_write_audio(d_mp.a_packet->data,d_mp.a_packet->size,d_mp.a_packet->pts);	
		}
		//*************************************
		av_packet_unref(d_mp.a_packet);
		//*************************************
	}

}

int music_decode_pcm(AVCodecContext *pCodecCtx_audio, AVFrame *frame, AVPacket *avpkt)
{
	int ret  = -1;	
	int data_size = 0;

	
	char error_msg[256] ={0};

	if(!pCodecCtx_audio)
		return -1;
	
	if(!frame)
		return -1;

	if(!avpkt)
		return -1;

	if(!avpkt->data)
		return -1;

	if(!avpkt->size)
		return -1;	

	//*********************************************************
	ret = avcodec_send_packet(pCodecCtx_audio, avpkt);
	av_packet_unref(avpkt);

	if (ret < 0){
		log_e("err:%s\n",av_err2str(ret));
		return -1;
	}
	//*********************************************************
	ret = 0;
	while(ret == 0)
	{
		ret = avcodec_receive_frame(pCodecCtx_audio, frame);
		if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
		{
			log_e("err:%s\n",av_err2str(ret));
			break;
		}
		else if(ret < 0)
		{
			log_e("err:%s\n",av_err2str(ret));
			break;
		}
		else
		{
			d_mp.pcm_size = frame->linesize[0];

			#if 0
			log_d("linesize:%d\n",frame->linesize[0]);
			log_d("format:%d\n",frame->format);
			log_d("nb_samples:%d\n",frame->nb_samples);
			log_d("nb_channels:%d\n",frame->ch_layout.nb_channels);
			log_d("pcm_size:%d\n",d_mp.pcm_size);
			#endif
												   
			if(d_mp.acvt_ctx&&d_mp.pcm_buff){
				ret = swr_convert(d_mp.acvt_ctx, &d_mp.pcm_buff, 192000, &frame->data, frame->nb_samples);
			}
		
			if(ret < 0){
				log_e("err:%s\n",av_err2str(ret));
				break;
			}else{
				queue_audio_pcm_write(d_mp.pcm_buff,d_mp.pcm_size,frame->pts);
			}
		}
	}

	return 0;
}


void* music_pcm_thread(void *arg)
{
	int ret = -1;
	
	int		  packet_size	= 0;
	int64_t	  packet_pts	= 0;
	uint8_t * packet_data	= NULL;

	while(1)
	{
		
		usleep(5*1000);
		//*****************************************************************
		ret = queue_audio_pcm_read(&packet_data,&packet_size,&packet_pts);
		if(ret < 0){	
			packet_pts  = 0;
			packet_size = 0;
			packet_data = NULL;

			if(d_mp.is_end == 1)
			{
				d_mp.is_end = 2;
				de_music_close();
				message_packet_send(MSG_VIDEO_END,0,0);
				log_e("music end\n");	
				pthread_exit(NULL);
			}
			continue;
		}

		d_mp.a_ctx->frame_size = packet_size/4;

		log_n("frame_size:%d\n",d_mp.a_ctx->frame_size);

		alsa_pcm_play_data(packet_data,d_mp.a_ctx->frame_size);
		//*****************************************************************
	}
}


void* music_de_thread(void *arg)
{
	int       ret = -1;
	int64_t   audio_pts   = 0;
	int64_t   packet_size = 0;
	uint8_t*  packet_data = NULL;

	while(1)
	{
		usleep(1000);
		//*****************************************************************
		ret = queue_packet_read_audio(&packet_data,&packet_size,&audio_pts);
		if(ret != 0){
			if(d_mp.is_end == 2)
			{
				pthread_exit(NULL);
			}
			continue;
		}
		//*****************************************************************

		d_mp.d_packet->data = packet_data;
		d_mp.d_packet->size = packet_size;
		d_mp.d_packet->pts  = audio_pts;
	
		music_decode_pcm(d_mp.a_ctx,d_mp.a_frame,d_mp.d_packet);	
		//*****************************************************************
	}

}

//****************************************************************
int de_music_file(const char *file_name)
{
	int i    = 0;
	int ret  = 0;
	int hours, mins, secs, us;
	
    AVDictionary * opts = NULL;

	av_dict_set(&opts, "rtsp_transport", "tcp", 0);
	av_dict_set(&opts, "scan_all_pmts", "1", AV_DICT_DONT_OVERWRITE);

	d_mp.is_end   = 0;
	d_mp.pcm_size = 0;
	//****************************************************************
	d_mp.a_format = avformat_alloc_context();
	if(!d_mp.a_format){
		log_e("avformat error\n");
		return -1;
	}
	//****************************************************************
	ret = avformat_open_input(&d_mp.a_format, file_name, NULL,&opts);
	if(ret != 0){
		log_e("open err=%s\n",file_name);
		avformat_free_context(d_mp.a_format);
		d_mp.a_format = NULL;
		return -1;
	}
	//****************************************************************
	ret = avformat_find_stream_info(d_mp.a_format, NULL);
	if(ret < 0){	
		log_e("no stream\n");

		avformat_close_input(&d_mp.a_format);
		avformat_free_context(d_mp.a_format);
		d_mp.a_format = NULL;
		return -1;
	}	
	//****************************************************************
	log_i("*****************************************\n");

	av_dump_format(d_mp.a_format,0, file_name,0);
	
	log_i("*****************************************\n");
	//****************************************************************
	d_mp.a_index = -1;

	log_d("bit_rate     =%d\n",d_mp.a_format->bit_rate);
	log_d("packet_size  =%d\n",d_mp.a_format->packet_size);
	log_d("nb_streams=%d\n",d_mp.a_format->nb_streams);

	
	//****************************************************************
	for(i = 0; i < d_mp.a_format->nb_streams; i++)
	{
		if (d_mp.a_format->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) 
		{
			d_mp.a_index = i;
		}
	}		

	log_d("a_index =%d\n",d_mp.a_index);
	//*************************************************************************************
	d_mp.a_duration = d_mp.a_format->duration;

	secs  	 = d_mp.a_duration / AV_TIME_BASE;
	us	     = d_mp.a_duration % AV_TIME_BASE;
	mins     = secs / 60;
	secs    %= 60;
	hours    = mins / 60;
	mins    %= 60;
	
	log_d("duration=%02d:%02d:%02d.%02d\n",hours,mins,secs,(100 * us) / AV_TIME_BASE);
	//*************************************************************************************
	if(d_mp.a_index == -1)
	{
		log_e("no video stream\n");		
		avformat_close_input(&d_mp.a_format);
		avformat_free_context(d_mp.a_format);
		d_mp.a_format = NULL;
		return -1;
	}
	//*************************************************************************************
	log_n("v_codec_id =%d\n",d_mp.a_format->streams[d_mp.a_index]->codecpar->codec_id);
	log_d("frame_size =%d\n",d_mp.a_format->streams[d_mp.a_index]->codecpar->frame_size);
	//*************************************************************************************
    d_mp.a_ctx = avcodec_alloc_context3(NULL);
    if(!d_mp.a_ctx){
		log_e("avcodec alloc error\n");
		return -1;
	}
	//*************************************************************************************
    ret = avcodec_parameters_to_context(d_mp.a_ctx, d_mp.a_format->streams[d_mp.a_index]->codecpar);
    if (ret < 0){
		log_e("avcodec parameters error\n");
		return -1;
	}
	//*************************************************************************************
    d_mp.a_codec = avcodec_find_decoder(d_mp.a_ctx->codec_id);
	if(d_mp.a_codec){
		log_d("audio name =%s\n",d_mp.a_codec->name);
	}
	//*************************************************************************************
	av_dict_set(&opts, "threads", "auto", 0);
	av_dict_set(&opts, "refcounted_frames", "1", 0);
	av_dict_set(&opts, "timeout", "1000", 0);
	//*************************************************************************************
	ret = avcodec_open2(d_mp.a_ctx, d_mp.a_codec, &opts);
	if(ret < 0){
		log_e("audio codec open error.\n");
		return -1;
	}
	//*************************************************************************************
	d_mp.a_packet = av_packet_alloc();
	if(!d_mp.a_packet){
		log_e("v packet error.\n");
		return -1;
	}
	av_init_packet(d_mp.a_packet);
	//****************************************************************
	d_mp.d_packet = av_packet_alloc();
	if(!d_mp.d_packet){
		log_e("a packet error.\n");
		return -1;
	}
	av_init_packet(d_mp.d_packet);
	//****************************************************************
	d_mp.a_frame = av_frame_alloc();
	if(!d_mp.a_frame){
		log_e("a frame error.\n");
		return -1;
	}
	//****************************************************************
	d_mp.pcm_buff	= (uint8_t*)av_malloc(192000 * 2);
	d_mp.acvt_ctx	= swr_alloc();
	if(!d_mp.acvt_ctx){
		log_e("swr alloc error\n");
		return -1;
	}

	d_mp.acvt_ctx = swr_alloc_set_opts(d_mp.acvt_ctx,AV_CH_LAYOUT_STEREO,AV_SAMPLE_FMT_S16, //AV_SAMPLE_FMT_S16
											d_mp.a_ctx->sample_rate,
											av_get_default_channel_layout(d_mp.a_ctx->channels), 
											d_mp.a_ctx->sample_fmt, 
											d_mp.a_ctx->sample_rate, 0, NULL);

	swr_init(d_mp.acvt_ctx);
	//****************************************************************
	log_d("sample_rate =%d\n",d_mp.a_ctx->sample_rate);	
	log_d("sample_fmt  =%d\n",d_mp.a_ctx->sample_fmt);	
	log_d("channels    =%d\n",d_mp.a_ctx->channels);
	log_d("frame_size  =%d\n",d_mp.a_ctx->frame_size);
	//****************************************************************
	alsa_cfg_channels(d_mp.a_ctx->channels);
	alsa_cfg_sample_fmt(d_mp.a_ctx->sample_fmt);
	alsa_cfg_sample_rate(d_mp.a_ctx->sample_rate);

	alsa_cfg_device();

	log_d("open file stream succeed.\n");
	//****************************************************************
	pthread_create(&d_mp.a_thread,NULL,music_read_thread,NULL);
	pthread_create(&d_mp.d_thread,NULL,music_de_thread,NULL);
	pthread_create(&d_mp.p_thread,NULL,music_pcm_thread,NULL);
	//****************************************************************

	return 0;
}
//****************************************************************
int de_music_close(void)
{

	queue_packet_deinit();
	queue_audio_pcm_deinit();
	//***************************************

	if(d_mp.a_ctx){
		avcodec_close(d_mp.a_ctx);
		avcodec_free_context(&d_mp.a_ctx);
		d_mp.a_codec = NULL;
	}
	//***************************************
	if(d_mp.a_packet){
		av_packet_free(d_mp.a_packet);
		d_mp.a_packet = NULL;
	}
	if(d_mp.d_packet){
		av_packet_free(d_mp.d_packet);
		d_mp.d_packet = NULL;
	}
	if(d_mp.a_frame){
		av_frame_free(&d_mp.a_frame);
		d_mp.a_frame = NULL;
	}
	//***************************************
	if(d_mp.a_format)
	{
		if(&d_mp.a_format)
			avformat_close_input(&d_mp.a_format);
		avformat_free_context(d_mp.a_format);
		d_mp.a_format = NULL;
	}
	//***************************************
	if(d_mp.pcm_buff){
		free(d_mp.pcm_buff);
		d_mp.pcm_buff = NULL;
	}
	if(d_mp.acvt_ctx){
		swr_free(&d_mp.acvt_ctx);
		d_mp.acvt_ctx = NULL;
	}
	//***************************************
	pthread_join(d_mp.a_thread, NULL);
	pthread_join(d_mp.d_thread, NULL);
	pthread_join(d_mp.p_thread, NULL);
	//***************************************

	log_d("close file stream succeed.\n");
	
	return 0;

}




